// @Author miaoqing
// @Date 2024/03/28 15:13:00
// @Desc
package logic

import (
	"core/command"
	"core/component/function"
	l4g "github.com/alecthomas/log4go"
	"sync"
	"time"
)

// GameState 游戏状态
type GameState int

const (
	k_Ready  GameState = 0 // 准备阶段
	k_Gaming           = 1 // 战斗中阶段
	k_Over             = 2 // 结束阶段
	k_Stop             = 3 // 停止
)

const (
	MaxReadyTime          int64  = 20            // 准备阶段最长时间，如果超过这个时间没人连进来直接关闭游戏
	MaxGameFrame          uint32 = 30*60*3 + 100 // 每局最大帧数
	BroadcastOffsetFrames        = 3             // 每隔多少帧广播一次
	kMaxFrameDataPerMsg          = 60            // 每个消息包最多包含多少个帧数据
	kBadNetworkThreshold         = 2             // 这个时间段没有收到心跳包认为他网络很差，不再持续给发包(网络层的读写时间设置的比较长，客户端要求的方案)
)

// Game 一局游戏
type Game struct {
	id               uint64
	startTime        int64
	randomSeed       int64
	State            GameState
	players          *sync.Map //map[uint64]*Player
	logic            *lockstep
	clientFrameCount uint32

	result map[uint64]uint64

	//listener gameListener

	dirty bool
	room  *Room
}

// NewGame 构造游戏
func newGame(id uint64, players []uint64, randomSeed int64, room *Room) *Game {
	g := &Game{
		id:         id,
		players:    &sync.Map{},
		logic:      newLockstep(),
		startTime:  time.Now().Unix(),
		randomSeed: randomSeed,
		room:       room,
		result:     make(map[uint64]uint64),
	}

	for k, v := range players {
		g.players.Store(v, newPlayer(v, int32(k+1)))
	}

	return g
}

// JoinGame 加入游戏
func (g *Game) joinGame(id uint64) bool {

	//msg := &command.S2C_ConnectMsg{
	//	ErrorCode: command.ERRORCODE_ERR_Ok,
	//}

	p := g.getGamePlayer(id)
	if p == nil {
		l4g.Error("[game(%d)] player[%d] join room failed", g.id, id)
		return false
	}

	if k_Ready != g.State && k_Gaming != g.State {
		//msg.ErrorCode = command.ERRORCODE_ERR_RoomState
		//p.sendMsg(command.ID_MSG_Connect, msg)
		l4g.Error("[game(%d)] player[%d] game is over", g.id, id)
		return true
	}

	// 把现有的玩家顶掉
	if p.isOnline {
		// TODO 这里有多线程操作的危险 如果调 p.client.Close() 会把现有刚进来的玩家提调
		//p.client.PutExtraData(nil)
		l4g.Error("[game(%d)] player[%d] replace", g.id, id)
	}

	//p.Connect(conn)

	//p.sendMsg(command.ID_MSG_Connect, msg)

	//g.listener.OnJoinGame(g.id, id)

	return true

}

// LeaveGame 离开游戏
func (g *Game) leaveGame(id uint64) bool {

	p := g.getGamePlayer(id)
	if p == nil {
		return false
	}
	p.cleanUp()

	//g.listener.OnLeaveGame(g.id, id)

	return true
}

// ProcessMsg 处理消息
//func (g *Game) ProcessMsg(id uint64, msg *pb_packet.Packet) {
//
//	player := g.getGamePlayer(id)
//	if !ok {
//		l4g.Error("[game(%d)] processMsg player[%d] msg=[%d]", g.id, player.id, msg.GetMessageID())
//		return
//	}
//	l4g.Info("[game(%d)] processMsg player[%d] msg=[%d]", g.id, player.id, msg.GetMessageID())
//
//	msgID := command.ID(msg.GetMessageID())
//
//	switch msgID {
//	case command.ID_MSG_JoinRoom:
//		msg := &command.S2C_JoinRoomMsg{
//			Roomseatid: player.idx,
//			RandomSeed: g.randomSeed,
//		}
//
//		for _, v := range g.players {
//			if player.id == v.id {
//				continue
//			}
//			msg.Others = append(msg.Others, v.id)
//			msg.Pros = append(msg.Pros, v.loadingProgress)
//		}
//
//		player.SendMessage(pb_packet.NewPacket(uint8(command.ID_MSG_JoinRoom), msg))
//
//	case command.ID_MSG_Progress:
//		if g.State > k_Ready {
//			break
//		}
//		m := &command.C2S_ProgressMsg{}
//		if err := msg.Unmarshal(m); nil != err {
//			l4g.Error("[game(%d)] processMsg player[%d] msg=[%d] UnmarshalPB error:[%s]", g.id, player.id, msg.GetMessageID(), err.Error())
//			return
//		}
//		player.loadingProgress = m.GetPro()
//		msg := pb_packet.NewPacket(uint8(command.ID_MSG_Progress), &command.S2C_ProgressMsg{
//
//			Id:  player.id,
//			Pro: m.Pro,
//		})
//		g.broadcastExclude(msg, player.id)
//
//	case command.ID_MSG_Heartbeat:
//		player.SendMessage(pb_packet.NewPacket(uint8(command.ID_MSG_Heartbeat), nil))
//		player.RefreshHeartbeatTime()
//	case command.ID_MSG_Ready:
//		if k_Ready == g.State {
//			g.doReady(player)
//		} else if k_Gaming == g.State {
//			g.doReady(player)
//			// 重连进来 TODO 对重连进行检查，重连比较耗费
//			g.doReconnect(player)
//			l4g.Warn("[game(%d)] doReconnect [%d]", g.id, player.id)
//		} else {
//			l4g.Error("[game(%d)] ID_MSG_Ready player[%d] state error:[%d]", g.id, player.id, g.State)
//		}
//
//	case command.ID_MSG_Input:
//		m := &command.C2S_InputMsg{}
//		if err := msg.Unmarshal(m); nil != err {
//			l4g.Error("[game(%d)] processMsg player[%d] msg=[%d] UnmarshalPB error:[%s]", g.id, player.id, msg.GetMessageID(), err.Error())
//			return
//		}
//		if !g.pushInput(player, m) {
//			l4g.Warn("[game(%d)] processMsg player[%d] msg=[%d] pushInput failed", g.id, player.id, msg.GetMessageID())
//			break
//		}
//
//		// 下一帧强制广播(客户端要求)
//		g.dirty = true
//	case command.ID_MSG_Result:
//		m := &command.C2S_ResultMsg{}
//		if err := msg.Unmarshal(m); nil != err {
//			l4g.Error("[game(%d)] processMsg player[%d] msg=[%d] UnmarshalPB error:[%s]", g.id, player.id, msg.GetMessageID(), err.Error())
//			return
//		}
//		g.result[player.id] = m.GetWinnerID()
//		l4g.Info("[game(%d)] ID_MSG_Result player[%d] winner=[%d]", g.id, player.id, m.GetWinnerID())
//		player.SendMessage(pb_packet.NewPacket(uint8(command.ID_MSG_Result), nil))
//	default:
//		l4g.Warn("[game(%d)] processMsg unknown message id[%d]", msgID)
//	}
//
//}

// Tick 主逻辑
func (g *Game) gameTicker() {
	now := time.Now().Unix()
	switch g.State {
	case k_Ready:
		delta := now - g.startTime
		if delta < MaxReadyTime {
			if g.checkReady() {
				g.doStart()
				g.State = k_Gaming
			}

		} else {
			if g.getOnlinePlayerCount() > 0 {
				// 大于最大准备时间，只要有在线的，就强制开始
				g.doStart()
				g.State = k_Gaming
				l4g.Warn("[game(%d)] force start game because ready state is timeout ", g.id)
			} else {
				// 全都没连进来，直接结束
				g.State = k_Over
				l4g.Error("[game(%d)] game over!! nobody ready", g.id)
			}
		}

		return
	case k_Gaming:
		if g.checkOver() {
			g.State = k_Over
			l4g.Info("[game(%d)] game over successfully!!", g.id)
			return
		}

		if g.isTimeout() {
			g.State = k_Over
			l4g.Warn("[game(%d)] game timeout", g.id)
			return
		}

		g.logic.tick()
		g.broadcastFrameData()

		return
	case k_Over:
		g.doGameOver()
		g.State = k_Stop
		l4g.Info("[game(%d)] do game over", g.id)
		return
	case k_Stop:
		return
	}

	return
}

// Result 战斗结果
func (g *Game) Result() map[uint64]uint64 {
	return g.result
}

// Close 关闭游戏
func (g *Game) Close() {
	//g.broadcast(command.ID_MSG_Close, nil)
}

// Cleanup 清理游戏
func (g *Game) Cleanup() {
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		p.cleanUp()
		return true
	})
	g.players = &sync.Map{}

}

func (g *Game) doReady(p *Player) {

	if p.isReady == true {
		return
	}

	p.isReady = true

	//msg := pb_packet.NewPacket(uint8(command.ID_MSG_Ready), nil)
	//p.sendMsg(command.ID_MSG_Ready, nil)
}

func (g *Game) checkReady() bool {
	isReady := true
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		if !p.isReady {
			isReady = false
			return false
		}
		return true
	})
	return isReady
}

func (g *Game) doStart() {

	g.clientFrameCount = 0
	g.logic.reset()
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		p.isReady = true
		p.loadingProgress = 100
		return true
	})
	g.startTime = time.Now().Unix()
	//msg := &command.SCSyncStartGame{
	//	TimeStamp: g.startTime,
	//}
	//ret := pb_packet.NewPacket(uint8(command.ID_MSG_Start), msg)

	//g.broadcast(command.ID_MSG_Start, msg)

	//g.room.OnGameStart(g.id)
}

func (g *Game) doGameOver() {
	g.room.Stop()
	//g.listener.OnGameOver(g.id)
}

func (g *Game) pushInput(p *Player, msg *command.CSInputFrameInfo) bool {

	cmd := &command.InputData{
		Id:         p.playerID,
		Sid:        msg.GetSid(),
		X:          msg.GetX(),
		Y:          msg.GetY(),
		Roomseatid: p.idx,
	}

	return g.logic.pushCmd(cmd)
}

func (g *Game) doReconnect(p *Player) {

	//msg := &command.S2C_StartMsg{
	//	TimeStamp: g.startTime,
	//}
	//ret := pb_packet.NewPacket(uint8(command.ID_MSG_Start), msg)
	//p.sendMsg(command.ID_MSG_Start, msg)

	framesCount := g.clientFrameCount
	var i uint32 = 0
	c := 0
	frameMsg := &command.SCSyncFrameData{}

	for ; i < framesCount; i++ {

		frameData := g.logic.getFrame(i)
		if nil == frameData && i != (framesCount-1) {
			continue
		}

		f := &command.FrameData{
			FrameID: i,
		}

		if nil != frameData {
			f.Input = frameData.cmds
		}
		frameMsg.Frames = append(frameMsg.Frames, f)
		c++

		if c >= kMaxFrameDataPerMsg || i == (framesCount-1) {
			//p.sendMsg(command.ID_MSG_Frame, frameMsg)
			c = 0
			frameMsg = &command.SCSyncFrameData{}
		}
	}

	p.SetSendFrameCount(g.clientFrameCount)

}

func (g *Game) broadcastFrameData() {

	framesCount := g.logic.getFrameCount()

	if !g.dirty && framesCount-g.clientFrameCount < BroadcastOffsetFrames {
		return
	}

	defer func() {
		g.dirty = false
		g.clientFrameCount = framesCount
	}()

	/*
		msg := &pb.S2C_FrameMsg{}

		for i := g.clientFrameCount; i < g.logic.getFrameCount(); i++ {
			frameData := g.logic.getFrame(i)

			if nil == frameData && i != (g.logic.getFrameCount()-1) {
				continue
			}

			f := &pb.FrameData{}
			f.FrameID = proto.Uint32(i)
			msg.Frames = append(msg.Frames, f)

			if nil != frameData {
				f.Input = frameData.cmds
			}

		}
		if len(msg.Frames) > 0 {
			g.broadcast(pb_packet.NewPacket(uint8(pb.ID_MSG_Frame), msg))
		}
	*/
	now := time.Now().Unix()

	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		// 掉线的
		if !p.IsOnline() {
			return true
		}

		if !p.isReady {
			return true
		}

		// 网络不好的
		if now-p.GetLastHeartbeatTime() >= kBadNetworkThreshold {
			return true
		}

		// 获得这个玩家已经发到哪一帧
		i := p.GetSendFrameCount()
		c := 0
		msg := &command.SCSyncFrameData{}
		for ; i < framesCount; i++ {
			frameData := g.logic.getFrame(i)
			if nil == frameData && i != (framesCount-1) {
				continue
			}

			f := &command.FrameData{
				FrameID: i,
			}

			if nil != frameData {
				f.Input = frameData.cmds
			}
			msg.Frames = append(msg.Frames, f)
			c++

			// 如果是最后一帧或者达到这个消息包能装下的最大帧数，就发送
			if i == (framesCount-1) || c >= kMaxFrameDataPerMsg {
				//p.sendMsg(command.ID_MSG_Frame, msg)
				c = 0
				msg = &command.SCSyncFrameData{}
			}

		}

		p.SetSendFrameCount(framesCount)
		return true
	})

}

func (g *Game) broadcast(cmd command.Command, msg interface{}) {
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		p.sendMsg(cmd, msg)
		return true
	})
}

func (g *Game) broadcastExclude(cmd command.Command, msg interface{}, id uint64) {
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		if p.playerID == id {
			return true
		}
		p.sendMsg(cmd, msg)
		return true
	})
}

func (g *Game) getPlayerCount() int {
	return int(function.CalcSyncMapLen(g.players))
}

func (g *Game) getOnlinePlayerCount() int {
	i := 0
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		if p.IsOnline() {
			i++
		}
		return true
	})
	return i
}

func (g *Game) checkOver() bool {
	// 只要有人没发结果并且还在线，就不结束
	isOver := true
	g.players.Range(func(key, value any) bool {
		p, ok := value.(*Player)
		if !ok {
			return true
		}
		if !p.isOnline {
			return true
		}
		if _, exist := g.result[p.playerID]; !exist {
			isOver = false
			return false
		}
		return true
	})
	return isOver
}

func (g *Game) isTimeout() bool {
	return g.logic.getFrameCount() > MaxGameFrame
}

func (g *Game) getGamePlayer(id uint64) *Player {
	val, ok := g.players.Load(id)
	if !ok {
		return nil
	}
	return val.(*Player)
}
